package com.ydlclass.ChannelHandler.handler.OutboundHandler;

import com.ydlclass.Transport.message.MessageFormatConstant;
import com.ydlclass.Transport.message.Request.RequestPayload;
import com.ydlclass.Transport.message.Request.YrpcRequest;
import com.ydlclass.compress.Compress;
import com.ydlclass.compress.CompressFactory;
import com.ydlclass.serialize.Serialize;
import com.ydlclass.serialize.SerializerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.List;

/**
 * Request请求编码器
 * 4B  magic(魔术)
 * 1B  version(版本)
 * 2B  header length (头部长度)
 * 4B  full  length (总长度)
 * 1B  request type (请求类型)
 * 1B  serialize (序列化类型)
 * 1B  compress (压缩类型)
 * 8B  requestId (请求ID)
 * @Author duWenJian
 * @Date 2024/2/21 19:46·
 * @Version 1.0
 */
@Slf4j
public class YrpcMessageRequestEncodeHandler extends MessageToMessageEncoder<YrpcRequest> {
    @Override
    protected void encode(ChannelHandlerContext ctx, YrpcRequest yrpcRequest, List<Object> out) {
        ByteBuf buf = ctx.alloc().buffer();
        // 魔术值 占4个字节
        buf.writeBytes(MessageFormatConstant.MAGIC);
        // 版本号 占1个字节
        buf.writeByte(MessageFormatConstant.VERSION);
        // 头部长度 占2个字节
        buf.writeShort(MessageFormatConstant.HEAD_LENGTH);
        // todo 总长度 占4个字节,不知道body的长度,
        //  保存当前写指针的位置 writeIndex(写指针) 往后移动4个位置，等body长度计算出来之后，再计算FULL_LENGTH
        buf.markWriterIndex();
        buf.writerIndex(buf.writerIndex() + MessageFormatConstant.FULL_FIELD_LENGTH);
        // 请求类型 占1个字节 序列化类型 占1个字节 压缩类型 占1个字节
        buf.writeByte(yrpcRequest.getRequestType());
        buf.writeByte(yrpcRequest.getSerializeType());
        buf.writeByte(yrpcRequest.getCompressType());
        // 请求Id 占8个字节
        buf.writeLong(yrpcRequest.getRequestId());
        // 时间戳
        buf.writeLong(yrpcRequest.getTimeStamp());
        // 如果是心跳请求,需要独立的处理一下总长度
//        if (RequestTypeEnum.HeartbeatRequest.getId() == yrpcRequest.getRequestType()){
//            int index = buf.writerIndex();
//            buf.resetWriterIndex();
//            buf.writeInt(MessageFormatConstant.HEAD_LENGTH);
//            buf.writerIndex(index);
//            out.add(buf);
//            return;
//        }

        byte[] body = null;
        if (yrpcRequest.getRequestPayload() != null){
        // 序列化
        Serialize serialize = SerializerFactory.getSerializer(yrpcRequest.getSerializeType()).getImpl();
        body = serialize.serialize(yrpcRequest.getRequestPayload());
        // 压缩
        Compress compress = CompressFactory.getCompress(yrpcRequest.getCompressType()).getImpl();
        body = compress.compress(body);
        }

        // 请求体payload 经过计算算出

        if (body != null){
            buf.writeBytes(body);
        }
        int bodyLength = body == null ? 0 : body.length;
        // 保存当前写指针的位置
        int index = buf.writerIndex();
        buf.resetWriterIndex();
        buf.writeInt(MessageFormatConstant.HEAD_LENGTH + bodyLength);
        // 写指针回来
        buf.writerIndex(index);
        out.add(buf);
    }

    private byte[] getBodyBytes(RequestPayload requestPayload) {
        //  todo 心跳请求没有payload
        if (requestPayload == null){
            return null;
        }
        // todo 将对象转化为字节数组  序列化(选择了jdk的), 压缩,
        //  针对不同的消息类型做不同的处理,如果payload是空的，心跳请求的请求，返回空的字节数组
        //  希望可以通过一些设计模式，oop的方式可以让我们修改序列化方式、压缩方式,自由配置
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(requestPayload);

            // todo 压缩

            return baos.toByteArray();
        } catch (IOException e) {
            log.error("序列化发生异常:{}",e);
            throw new RuntimeException("对body进行序列化发生错误");
        }
    }
}
